--- %%NOBANNER%% -->
/*------------------<--- Start of Description -->--------------------\
| Returns Schoenfeld residuals and scaled Schoenfeld residuals from |
| the Cox model. The Schoenfeld residuals(r) are defined only at |
| event times. There is one residual for each covariate in the Cox |
| model. The scaled residuals(Bt) are defined as: |
| Bt= B + D*cov(B)*r', B=cox model beta, |
| D=total #events and |
| r=Schoenfeld residuals. |
| Bt is an estimate of the hazard function at follow-up time t. |
| Therefore a plot of Bt vs time can be used to assess whether the |
| proportional hazards assumption is valid. As time is frequently |
| skewed, plots of Bt vs rank time or '1-Kaplan-Meier' for the entire|
| dataset(which is a monotone function of time) may be preferred. |
| The correlation of Bt with time provides a test of the |
| proportional hazards assumption. The Breslow method is used for |
| tied event times. |
|--------------------<--- End of Description -->---------------------|
|--------------------------------------------------------------------|
|--------------<--- Start of Files or Arguments Needed -->-----------|
| Arguments: |
| - Required: |
| time=time variable for survival |
| event=event variable for survival, 1=event, 0=censored |
| xvars=list of covariates for Cox model |
| - Optional: |
| data= name of analysis dataset. Default is last dataset |
| created. |
| strata=stratification variable(one only) for stratifed Cox |
| models. |
| outsch=name of output data set containing Schoenfeld |
| residuals. One obs per each event time. The variables |
| containing the residuals have the same name as the |
| covariates (xvars). The data set also includes the time|
| variable and the strata variable. |
| Default data set name is schr. |
| outbt =name of output data set containing the scaled Schoenfeld|
| residuals(Bt). One obs per each event time. The |
| variables containing the scaled residuals have the same |
| name as the covariates (xvars). The dataset also |
| includes the time variable, the rank of the time(rtime) |
| and a variable(probevt) which is equal to '1 - overall |
| Kaplan-Meier' at the given time. |
| Default data set name is schbt. |
| plot=t,r,k,n. Indicates that SAS/Graph plots of Bt vs |
| time(t), rank of time(r) or '1 - overall Kaplan-Meier' |
| (k) are to be done. Default is r. For no plots use |
| n. The name of the graphics catalog is gschbt. |
| vref= indicator to control plotting of a vertical reference |
| line at y=0. Values are yes(default) and no. |
| points=yes,no. Whether to plot the actual data points. |
| Default is yes. |
| df= degrees of freedom for smoothing process Possible |
| values are 3 - 7. Default is 4. |
| pvars= variables to plot. Default is all xzvars. |
| alpha= confidence coefficient for plotting standard |
| error bars. Default is .05. A value of 0 means |
| do not plot SE bars. |
| rug= indicator to control plotting of rug of x values. |
| Values are yes and no(default). |
| Output: The macro prints the PHREG output used to fit the Cox |
| model, correlation coefficients of Bt vs time and |
| SAS/Graph plots of Bt vs time. |
|---------------<--- End of Files or Arguments Needed -->------------|
|--------------------------------------------------------------------|
|----------------<--- Start of Example and Usage -->-----------------|
| Usage: %schoen(data=_last_,time=,event=,xvars=,vref=yes, |
| strata=,outsch=schr,outbt=schbt,plot=r,points=yes, |
| df=4,pvars=,alpha=.05,rug=no); |
| References: Schoenfeld, D. (1982). Partial residuals for the |
| proportional hazards regression model. Biometrika 69, 239-41. |
| Grambsch PM, Therneau TM (1993). Proportional hazards tests |
| and diagnostics based on weighted residuals. University of |
| Minnesota, Division of Biostatistics. Research Report 93-002. |
\-------------------<--- End of Example and Usage -->---------------*/
%macro schoen(data=_last_,time=,event=,xvars=,vref=yes,
strata=,outsch=schr,outbt=schbt,plot=r,points=yes,
df=4,pvars=,alpha=.05,rug=no);
/*--------------------------------------------\
| Author: E. Bergstralh and T. Therneau; |
| Created: May 6, 1993; |
| Purpose: Returns Schoenfeld residuals and |
| scaled Schoenfeld residuals from |
| the Cox model; |
\--------------------------------------------*/
footnote"schoen macro: event=&event time=&time strata=&strata";
footnote2"Xvars= &xvars";
%let bad=0;
%if &time= %then %do;
%put ERROR: NO TIME VARIABLE GIVEN.;
%let bad=1;
%end;
%if &event= %then %do;
%put ERROR: NO EVENT VARIABLE GIVEN.;
%let bad=1;
%end;
%if &xvars= %then %do;
%put ERROR: NO XVARS GIVEN.;
%let bad=1;
%end;
%if &df<3 | &df>7 %then %do;
%put ERROR: DF MUST BE BETWEEN 3 AND 7.;
%let bad=1;
%end;
%if %upcase(&points)^=YES & %upcase(&points)^=NO %then %do;
%put ERROR: POINTS MUST BE YES OR NO.;
%let bad=1;
%end;
%if %upcase(&rug)^=YES & %upcase(&rug)^=NO %then %do;
%put ERROR: RUG MUST BE YES OR NO.;
%let bad=1;
%end;
%if %upcase(&vref)^=YES & %upcase(&vref)^=NO %then %do;
%put ERROR: VREF MUST BE YES OR NO.;
%let bad=1;
%end;
%if %upcase(&plot)^=T & %upcase(&plot)^=R & %upcase(&plot)^=K &
%upcase(&plot)^=N %then %do;
%put ERROR: PLOT MUST BE T, R, K, OR N.;
%let bad=1;
%end;
%let badalpha=0;
data _null_;
a=symget('alpha');
if a<0 | a>=1 then call symput('badalpha',1);
if a=0 then doalpha=0;
else doalpha=1;
call symput('doalpha',doalpha);
run;
%if &badalpha=1 %then %do;
%put ERROR: ALPHA MUST BE BETWEEN 0 AND 1;
%let bad=1;
%end;
%let nxs=0; %*count the number of x vars;
%do %until(%scan(&xvars,&nxs+1,' ')= );
%let nxs=%eval(&nxs+1);
%end;
%if &pvars^= %then %let plotvars=&pvars;
%else %let plotvars=&xvars;
%let nplotvar=0;
%do %until(%scan(&plotvars,&nplotvar+1,' ')= );
%let nplotvar=%eval(&nplotvar+1);
%end;
%do i=1 %to &nplotvar;
%let pv=%scan(&plotvars,&i,' ');
%let found=0;
%do j=1 %to &nxs;
%if &pv=%scan(&xvars,&j,' ') %then %let found=1;
%end;
%if &found=0 %then %do;
%put ERROR: PLOT VARIABLE &PV NOT ON XVARS LIST.;
%let bad=1;
%end;
%end;
%macro xlist(prefix); %*set-up var list code;
%do j=1 %to &nxs;
&prefix&j
%end;
%mend xlist;
%if &bad=0 %then %do;
data _setup; set &data; %*delete obs with mising values;
xmiss=0;
%do i=1 %to &nxs;
xx=%scan(&xvars,&i);
if xx=. then xmiss=1;
%end;
if &event=. or &time=. or xmiss=1 then delete;
%* run phreg and grab output datasets;
proc phreg data=_setup covout outest=_est ;
model &time*&event(0)= &xvars ;
%if &strata ^= %then %do;
strata &strata;
%end;
output out=_sch xbeta=xbeta ressch=rsch1-rsch&nxs
wtressch=wrsch1-wrsch&nxs;
data _sch1; set _sch(keep=&strata &time &event rsch1-rsch&nxs);
drop &event;
if &event=1;
rename
%do i=1 %to &nxs;
%let thisx=%scan(&xvars,&i,' ');
rsch&i=&thisx
%end;
;
proc sort; by &time;
data &outsch; set _sch1;
proc sort; by &strata &time;
** calculate overall Kaplan-Meier;
proc lifetest noprint data=_setup outs=_km;
time &time*&event(0);
data _km2; set _km;
keep &time probevt;
if _censor_=0; **keep event times;
probevt=1-survival;
label probevt='1-Overall Kaplan-Meier';
data _null_;
set _est;
%if %substr(&sysver,1,1)=6 %then %do;
if _type_='PARMS' & _name_='ESTIMATE';
%end;
%if %substr(&sysver,1,1)=8 %then %do;
if _type_='PARMS' & upcase(_name_)=upcase("&time") ;
%end;
%do i=1 %to &nxs;
%let thisx=%scan(&xvars,&i,' ');
call symput("est&i",&thisx);
%end;
data _sch2;
set _sch(keep=&strata &time &event wrsch1-wrsch&nxs);
keep &strata &time &xvars;
if &event=1;
%do i=1 %to &nxs;
%let thisx=%scan(&xvars,&i,' ');
&thisx=&&est&i + wrsch&i;
%end;
proc sort; by &time;
data _bt;
merge _sch2(in=ins) _km2(keep=&time probevt);
by &time;
if ins;
proc sort; by &strata &time;
run;
symbol1 i=join l=1 c=black;
symbol2 i=join l=2 c=black;
symbol3 i=join l=2 c=black;
symbol4 i=none v=dot h=.1 cm c=black;
%macro dovars(tvar);
%if %length(&tvar)=8 %then %let t=%substr(&tvar,1,7);
%else %let t=&tvar;
%daspline(&tvar,nk=&df,data=&outbt)
%do i=1 %to &nplotvar;
%let x=%scan(&plotvars,&i,' ');
data __temp1;
set &outbt;
&&_&t
%global _print_;
%let _print_=off;
%let ndummies=%eval(&df-2);
%if %substr(&sysver,1,1)=8 %then %do;
ods listing close;
%end;
proc genmod;
model &x=&tvar &t.1-&t.&ndummies/dist=normal obstats
%if &doalpha=1 %then %do;
alpha=&alpha
%end;
;
make 'OBSTATS' out=__temp2;
run;
%if %substr(&sysver,1,1)=8 %then %do;
ods listing;
%end;
data __temp3;
merge __temp1 __temp2;
/* lower=xbeta-2*std;
upper=xbeta+2*std; */
%if %upcase(&rug)=YES %then %do;
data __anno;
set __temp3;
x=&tvar;
y=0;
xsys='2';
ysys='1';
function='move';
output;
y=5;
function='draw';
output;
%end;
proc sort data=__temp3; by &tvar;
proc gplot data=__temp3 gout=gschbt
%if %upcase(&rug)=YES %then %do;
annotate=__anno
%end;
;
%if &doalpha=1 %then %do;
plot xbeta*&tvar=1
lower*&tvar=2
upper*&tvar=3
%if %upcase(&points)=YES %then %do;
&x*&tvar=4
%end;
/overlay vaxis=axis1 haxis=axis2
%if %upcase(&vref)=YES %then %do;
vref=0 lvref=3
%end;
;
%end;
%if &doalpha=0 %then %do;
plot xbeta*&tvar=1
%if %upcase(&points)=YES %then %do;
&x*&tvar=4
%end;
/overlay vaxis=axis1 haxis=axis2
%if %upcase(&vref)=YES %then %do;
vref=0 lvref=3
%end;
;
%end;
axis1 label=(r=0 a=90 "smooth(&x) df=&df");
%if &tvar=&time %then %do;
axis2 label=("&tvar");
%end;
%if &tvar=rtime %then %do;
axis2 label=("Rank for Variable &time");
%end;
%if &tvar=probevt %then %do;
axis2 label=('1-Overall Kaplan-Meier');
%end;
run; quit;
%end;
%mend dovars;
title2'Scaled residuals(Bt) as a fcn of time.';
proc rank data=_bt out=&outbt; var &time; ranks rtime;
proc corr pearson data=&outbt;
with &xvars; var &time rtime probevt;
label probevt='1 - Overall Kaplan-Meier';
%if %upcase(&plot)=T %then %do;
%dovars(&time)
%end;
%if %upcase(&plot)=R %then %do;
%dovars(rtime)
%end;
%if %upcase(&plot)=K %then %do;
%dovars(probevt)
%end;
run;
quit;
footnote; symbol; title2;
proc datasets nolist;
delete _setup _est _sch _sch1 _sch2 _km _km2 _bt
%if %upcase(&rug)=YES %then %do;
__anno
%end;
__temp1 __temp2 __temp3;
run;
quit;
%end;
%mend schoen;